package com.example.demo.node;

import cn.hutool.core.util.RandomUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class NodeTest {

    static class Node {
        private String id;
        private String parentId;
        private List<Node> children;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getParentId() {
            return parentId;
        }

        public void setParentId(String parentId) {
            this.parentId = parentId;
        }

        public List<Node> getChildren() {
            return children;
        }

        public void setChildren(List<Node> children) {
            this.children = children;
        }
    }

    public static Integer TREE_LEVEL = 0;

    public static void main(String[] args) {
        int level = 7;
        int num = 100;
        int levelCount = 10;
        // 生成并展开树 level 2-8，每层级1-10个子层级
        Map<Integer, List<List<Node>>> levelTreeMap = createTree(level, num, levelCount);

        // 根据展开的树，组装树
        System.out.println("根据展开的树，组装树");
        List<Double> quoteTimeList = new ArrayList<>();
        for (Map.Entry<Integer, List<List<Node>>> entry : levelTreeMap.entrySet()) {
            System.out.println("level：   " + entry.getKey());
            for (List<Node> nodeList : entry.getValue()) {

                // 使用java的对象引用，组装树
                long time3 = Calendar.getInstance().getTimeInMillis();
                Node node1 = quoteAssembleTree(nodeList);
                long time4 = Calendar.getInstance().getTimeInMillis();
//              System.out.println(JSONUtil.toJsonStr(node1));
                System.out.println("引用耗时：" + (time4 - time3) + "ms");
                quoteTimeList.add((double) (time4 - time3));
            }
        }

        // 根据展开的树，组装树
        System.out.println("根据展开的树，组装树");
        List<Double> recursionTimeList = new ArrayList<>();
        for (Map.Entry<Integer, List<List<Node>>> entry : levelTreeMap.entrySet()) {
            System.out.println("level：   " + entry.getKey());
            for (List<Node> nodeList : entry.getValue()) {
                // 传统递归方式，组装树
                // 根节点id为0,就不从nodeList里面取了，直接new一个
                Node topNode = new Node();
                topNode.setId("0");
                long time1 = Calendar.getInstance().getTimeInMillis();
                recursionAssembleTree(topNode, nodeList);
                long time2 = Calendar.getInstance().getTimeInMillis();
//        System.out.println(JSONUtil.toJsonStr(topNode));
                System.out.println("递归耗时：" + (time2 - time1) + "ms");
                recursionTimeList.add((double) (time2 - time1));
            }
        }
        createPng(quoteTimeList, recursionTimeList, "深度：" + level + "；循环次数：" + num + "；每层级最多节点：" + levelCount, "深度：" + level + "；循环次数：" + num + "；每层级最多节点：" + levelCount);
    }

    private static Map<Integer, List<List<Node>>> createTree(int level, int num, int levelCount) {
        System.out.println("生成并展开树");
        Map<Integer, List<List<Node>>> levelTreeMap = new HashMap<>();
        for (int i = level; i < level + 1; i++) {
            TREE_LEVEL = i;
            System.out.println("level：" + i);

            List<List<Node>> nodes = new ArrayList<>();
            for (int j = 0; j < num; j++) {
                // 递归随机生成树
                Node node = new Node();
                node.setId("0");
                createTree(node, 1, levelCount);
//              System.out.println(JSONUtil.toJsonStr(node));

                // 递归展开树
                List<Node> nodeList = new ArrayList<>();
                long time5 = Calendar.getInstance().getTimeInMillis();
//                recursionOpenTree(node.getChildren(), nodeList);
                quoteOpenTree(node, nodeList);
                long time6 = Calendar.getInstance().getTimeInMillis();
//                System.out.println("递归展开树耗时：" + (time6 - time5) + "ms");
                System.out.println("引用展开树耗时：" + (time6 - time5) + "ms");
                nodes.add(nodeList);
            }
            levelTreeMap.put(i, nodes);
        }
        return levelTreeMap;
    }

    private static void quoteOpenTree(Node node, List<Node> nodeList) {
        nodeList.add(node);
        if (ObjectUtils.isEmpty(node.getChildren())) {
            return;
        }
        List<Node> children = node.getChildren();
        List<Node> nodes = new ArrayList<>(children);
        node.setChildren(null);
        for (Node node1 : nodes) {
            quoteOpenTree(node1, nodeList);
        }
    }

    private static void createPng(List<Double> h1, List<Double> h2, String title, String fileName) {
        try {
            List<String> categorie = new ArrayList<>();
            List<Serie> series = null;
            for (int i = 1; i < 101; i++) {
                categorie.add(String.valueOf(i));
            }
            //横坐标
            series = new Vector<Serie>();
            // 柱子名称：柱子所有的值集合
            //纵坐标
            Double[] h1a = new Double[h1.size()];
            h1.toArray(h1a);
            Double[] h2a = new Double[h2.size()];
            h2.toArray(h2a);
            series.add(new Serie("引用", h1a));
            series.add(new Serie("递归", h2a));

            //将图片保存为png格式
            CreatLineChart.CreateNewLineChartForPng(title, "", "NO_DATA_MSG", "./" + fileName + ".png", categorie, series, 900, 500);

        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    private static Node quoteAssembleTree(List<Node> allNodeList) {
//        List<String> collect = allNodeList.stream().map(Node::getId).collect(Collectors.toList());
//        System.out.println("+++++"+JSONUtil.toJsonStr(collect));
        Map<String, Node> nodeMap = allNodeList.stream().collect(Collectors.toMap(Node::getId, Function.identity()));
        for (Node node : allNodeList) {
            if (node.getParentId() != null && nodeMap.containsKey(node.getParentId())) {
                List<Node> children = nodeMap.get(node.getParentId()).getChildren();
                if (CollectionUtils.isEmpty(children)) {
                    children = new ArrayList<>();
                }
                children.add(node);
                nodeMap.get(node.getParentId()).setChildren(children);
            }
        }
        return nodeMap.get("0");
    }


    private static void recursionAssembleTree(Node node, List<Node> allNodeList) {
        List<Node> children = new ArrayList<>();
        for (Node node1 : allNodeList) {
            if (!StringUtils.isEmpty(node1.getParentId()) && node1.getParentId().equals(node.getId())) {
                children.add(copyNode(node1));
            }
        }
        if (CollectionUtils.isEmpty(children)) {
            return;
        }
        node.setChildren(children);
        for (Node child : children) {
            recursionAssembleTree(child, allNodeList);
        }
    }

    // 递归拉平展开树
    public static void recursionOpenTree(List<Node> children, List<Node> nodeList) {
        for (Node childNode : children) {
            nodeList.add(copyNode(childNode));
            if (!CollectionUtils.isEmpty(childNode.getChildren())) {
                recursionOpenTree(childNode.getChildren(), nodeList);
            }
        }
    }

    // 复制本节点
    private static Node copyNode(Node node) {
        Node node1 = new Node();
        node1.setId(node.getId());
        node1.setParentId(node.getParentId());
        return node1;
    }

    // 递归生成树
    public static void createTree(Node node, Integer level, int levelCount) {
        if (level.equals(TREE_LEVEL)) {
            return;
        }
        level++;
        int childCount = RandomUtil.randomInt(1, levelCount);
        List<Node> children = new ArrayList<>();
        for (int i = 0; i < childCount; i++) {
            Node childNode = new Node();
            childNode.setId(RandomUtil.randomString(10));
            childNode.setParentId(node.getId());
            children.add(childNode);
            createTree(childNode, level, levelCount);
        }
        node.setChildren(children);
    }

}
